package com.duniptech.soa.simulation;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Set;
import java.util.Map.Entry;

import javax.xml.transform.Result;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.duniptech.soa.service.RTSimulationService;
import com.duniptech.soa.service.RTSimulationServiceInterface;
import com.duniptech.soa.util.Util;

public class RTCoordinator implements Runnable {
	//private String name;
	private Element xmlModel;
	private HashMap<String, RTSimulationServiceInterface> simulators;
	private Thread myThread;
	private long observeMillis;
	
	public RTCoordinator(String name, Element xmlCoupled) throws Exception {
		//this.name = name;
		this.xmlModel = xmlCoupled;
		
		simulators = new HashMap<String, RTSimulationServiceInterface>();
		myThread = new Thread(this);

		// Initialize simulators
		NodeList xmlComponentList = xmlModel.getChildNodes();
		for(int i=0; i<xmlComponentList.getLength(); ++i) {
			Node xmlComponentAsNode = xmlComponentList.item(i);
			if(xmlComponentAsNode.getNodeType()==Node.ELEMENT_NODE) {
				Element xmlComponent = (Element)xmlComponentAsNode;
				String nodeName = xmlComponent.getNodeName();
				if(nodeName.equals("coupled") || nodeName.equals("atomic")) {
					String nameComponent = xmlComponent.getAttribute("name");
					String hostComponent = xmlComponent.getAttribute("host");
					StringWriter writer = new StringWriter();  
					Result result = new StreamResult(writer);  
					TransformerFactory.newInstance().newTransformer().transform(new DOMSource(xmlComponent),result);
					String xmlComponentAsString = writer.toString();
			
					RTSimulationServiceInterface simulationService = new RTSimulationService(hostComponent + Util.RTSIMULATION_SERVICE);
					simulationService.newSimulator(xmlComponentAsString);
					simulators.put(nameComponent, simulationService);
				}
				else if(nodeName.equals("connection")) {
					String simulatorFrom = xmlComponent.getAttribute("atomicFrom");
					String portFrom = xmlComponent.getAttribute("portFrom");
					String simulatorTo = xmlComponent.getAttribute("atomicTo");
					String portTo = xmlComponent.getAttribute("portTo");
					
					RTSimulationServiceInterface simulationServiceFrom = simulators.get(simulatorFrom);
					RTSimulationServiceInterface simulationServiceTo = simulators.get(simulatorTo);
					simulationServiceFrom.addConnection(simulatorFrom, simulatorFrom, portFrom, simulatorTo, simulationServiceTo.getEndPoint(), portTo);					
				}
			}
		}
		
		initialize();
	}

	
	public void initialize() throws Exception {
		Set<Entry<String, RTSimulationServiceInterface>> pairs = simulators.entrySet();
		for(Entry<String, RTSimulationServiceInterface> pair : pairs) {
			pair.getValue().initialize(pair.getKey());
		}
	}

	
	public Thread getMyThread() { return myThread; }
	
	public void run() {
		try {
			if(Util.debug) System.out.println("RT Coordinator thread sleeping for: " + observeMillis + " ms.");
			Thread.sleep(observeMillis);
			if(Util.debug) System.out.println("RT Coordinator wakes up. Exit simulation.");
		} 
		catch (Exception ee) {
			System.out.println("RTCoordinator.run: " + ee.getLocalizedMessage());
		}
	}
	
	// observeTime in seconds
	public String observe(double observeTime) throws Exception {
		if(Util.debug) System.out.println("Starting simulation.");
		observeMillis = (long)(1000*observeTime);
		Set<Entry<String, RTSimulationServiceInterface>> pairs = simulators.entrySet();
		for(Entry<String, RTSimulationServiceInterface> pair : pairs) {
			pair.getValue().simulate(pair.getKey());
		}
		myThread.start();
		if(Util.debug) System.out.println("RT Coordinator thread started.");
		return "Coordinator started. Waiting for the results.\n";
	}


	public String getConsole(String clientIp) throws Exception {
		StringBuffer console = new StringBuffer();
		Set<Entry<String, RTSimulationServiceInterface>> pairs = simulators.entrySet();
		for(Entry<String, RTSimulationServiceInterface> pair : pairs) {
			console.append(pair.getValue().getConsole(clientIp));			
		}
		return console.toString();
	}


	public void exit() throws Exception {
		Set<Entry<String, RTSimulationServiceInterface>> pairs = simulators.entrySet();
		for(Entry<String, RTSimulationServiceInterface> pair : pairs) {
			pair.getValue().exit(pair.getKey());
		}
		simulators.clear();
		if(myThread!=null) myThread.interrupt();
		System.gc();
	}

}
